package com.ahccc.codegenerator.backend.jpa;

import com.ahccc.codegenerator.backend.IBackendGenerator;
import com.ahccc.codegenerator.backend.MySqlDbColumn;
import com.ahccc.codegenerator.config.GenerateConfig;
import com.ahccc.codegenerator.entity.Task;
import com.ahccc.common.util.DateUtil;
import com.ahccc.common.util.SpringUtil;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.support.JdbcUtils;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.*;

import static org.hibernate.cfg.AvailableSettings.USER;

/**
 * @copyright:ahccc
 * @Author:liukui
 * @Date: 2019/6/5 0005 11:31
 * @Description:
 */
public class JpaBackendGenerate implements IBackendGenerator {

    protected Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 生成父项目
     *
     * @param task
     * @return
     */
    @Override
    public void generateParent(Task task) {
        GenerateConfig generateConfig = SpringUtil.getBean(GenerateConfig.class);
        String groupId = task.getGroupId();
        String artifactId = task.getGroupId() + "." + task.getName() + ".parent";
        String serverName = task.getGroupId() + "." + task.getName() + ".server";
        String webName = task.getGroupId() + "." + task.getName() + ".web";
        String parentFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId;
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("groupId", groupId);
        params.put("artifactId", artifactId);
        params.put("serverName", serverName);
        params.put("webName", webName);
        generateFile(params, Const.JPA_TEMPLATE_PARENT, "pom.xml.ftl", parentFilePath, "pom.xml");
    }

    /**
     * 生成服务端项目
     *
     * @param task
     * @param selectedTableNames
     * @return
     */
    @Override
    public void generateServer(Task task, String[] selectedTableNames) {
        GenerateConfig generateConfig = SpringUtil.getBean(GenerateConfig.class);
        String groupId = task.getGroupId();
        String moduleName = task.getModuleName();
        String artifactId = task.getGroupId() + "." + task.getName() + ".server";
        String serverFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId;
        String mainJavaFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.MAIN_JAVA + artifactId.replaceAll("\\.", "/");
        String testJavaFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.TEST_JAVA + artifactId.replaceAll("\\.", "/");
        String modelFilePath = mainJavaFilePath + "/model/" + moduleName;
        String daoFilePath = mainJavaFilePath + "/repository/" + moduleName;
        String serviceFilePath = mainJavaFilePath + "/service/" + moduleName;
        String serviceImplFilePath = mainJavaFilePath + "/service/" + moduleName + "/impl";
        String controllerFilePath = mainJavaFilePath + "/controller/" + moduleName;
        String mainResourcesFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.MAIN_RESOURCES;
        String parentName = task.getGroupId() + "." + task.getName() + ".parent";
        String copyright = generateConfig.getCopyright();
        String author = task.getAuthor();
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("groupId", groupId);
        params.put("artifactId", artifactId);
        params.put("parentName", parentName);
        params.put("copyright", copyright);
        params.put("author", author);
        params.put("datetime", DateUtil.formatDateByFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
        params.put("moduleName", moduleName);
        params.put("applicationName", artifactId.replaceAll("\\.", "-"));

        String dbDriver = "";
        String dbUrl = "";
        String dbUserName = task.getDbUsername();
        String dbPassword = task.getDbPassword();
        switch (task.getDbType()) {
            case "mysql":
                dbDriver = "com.mysql.cj.jdbc.Driver";
                dbUrl = "jdbc:mysql://" + task.getDbHost() + ":" + task.getDbPort() + "/" + task.getDbSchema() + "?characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true";
                for (String table : selectedTableNames) {
                    List<MySqlDbColumn> list = infoMySqlTable(dbDriver, dbUrl, dbUserName, dbPassword, task.getDbSchema(), table);

                    String className = MySqlDbColumn.getFixName(table);
                    params.put("sname", className);
                    className = className.toUpperCase().substring(0, 1) + className.substring(1);
                    params.put("tablename", table);
                    params.put("name", className);
                    params.put("fields", list);
                    MySqlDbColumn pkField = new MySqlDbColumn();
                    List<MySqlDbColumn> pks = new LinkedList<MySqlDbColumn>();
                    for (MySqlDbColumn c : list) {
                        if (c.isPk()) {
                            pkField = c;
                            pks.add(c);
                        }
                    }
                    params.put("pks", pks);
                    params.put("pk", pkField);
                    generateFile(params, Const.JPA_TEMPLATE_SERVER, "model.java.ftl", modelFilePath, className + ".java");
                    generateFile(params, Const.JPA_TEMPLATE_SERVER, "dao.java.ftl", daoFilePath, className + "Dao.java");
                    generateFile(params, Const.JPA_TEMPLATE_SERVER, "service.java.ftl", serviceFilePath, className + "Manager.java");
                    generateFile(params, Const.JPA_TEMPLATE_SERVER, "serviceimpl.java.ftl", serviceImplFilePath, className + "ManagerImpl.java");
                    generateFile(params, Const.JPA_TEMPLATE_SERVER, "controller.java.ftl", controllerFilePath, className + "Controller.java");
                }
                break;
            case "sqlserver":
                break;
            case "oracle":
                break;
            default:
                dbDriver = "com.mysql.cj.jdbc.Driver";
                dbUrl = "jdbc:mysql://" + task.getDbHost() + ":" + task.getDbPort() + "/" + task.getDbSchema() + "?characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true";
                break;
        }

        params.put("dbDriver", dbDriver);
        params.put("dbUrl", dbUrl);
        params.put("dbUserName", dbUserName);
        params.put("dbPassword", dbPassword);
        //生成pom.xml
        generateFile(params, Const.JPA_TEMPLATE_SERVER, "pom.xml.ftl", serverFilePath, "pom.xml");

        generateFile(params, Const.JPA_TEMPLATE_SERVER, "Application.java.ftl", mainJavaFilePath, "Application.java");

        generateFile(params, Const.JPA_TEMPLATE_SERVER, "application.yml.ftl", mainResourcesFilePath, "bootstrap.yml");

        generateFile(params, Const.JPA_TEMPLATE_SERVER, "ApplicationTests.java.ftl", testJavaFilePath, "ApplicationTests.java");

        generateFile(params, Const.JPA_TEMPLATE_SERVER, "ServletInitializer.java.ftl", mainJavaFilePath, "ServletInitializer.java");
    }

    /**
     * 生成web项目，主要涉及后台代码
     *
     * @param task
     * @return
     */
    @Override
    public void generateWeb(Task task, String[] selectedTableNames) {
        GenerateConfig generateConfig = SpringUtil.getBean(GenerateConfig.class);
        String groupId = task.getGroupId();
        String moduleName = task.getModuleName();
        String artifactId = task.getGroupId() + "." + task.getName() + ".web";
        String webFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId;
        String mainJavaFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.MAIN_JAVA + artifactId.replaceAll("\\.", "/");
        String testJavaFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.TEST_JAVA + artifactId.replaceAll("\\.", "/");
        String controllerFilePath = mainJavaFilePath + "/controller/" + moduleName;
        String mainResourcesFilePath = generateConfig.getPath() + task.getName() + "/" + artifactId + Const.MAIN_RESOURCES;
        String parentName = task.getGroupId() + "." + task.getName() + ".parent";
        String copyright = generateConfig.getCopyright();
        String author = task.getAuthor();
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("groupId", groupId);
        params.put("artifactId", artifactId);
        params.put("parentName", parentName);
        params.put("copyright", copyright);
        params.put("author", author);
        params.put("datetime", DateUtil.formatDateByFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
        params.put("moduleName", moduleName);
        params.put("applicationName", artifactId.replaceAll("\\.", "-"));

        List<Map<String, Object>> tableList=new ArrayList<Map<String, Object>>();
        for (String table : selectedTableNames) {
            String tableName = MySqlDbColumn.getFixName(table);
            Map<String, Object> map=new HashMap<>();
            map.put("name",tableName);
            tableList.add(map);
        }
        params.put("tableList",tableList);
        //生成pom.xml
        generateFile(params, Const.JPA_TEMPLATE_WEB, "pom.xml.ftl", webFilePath, "pom.xml");

        generateFile(params, Const.JPA_TEMPLATE_WEB, "Application.java.ftl", mainJavaFilePath, "Application.java");

        generateFile(params, Const.JPA_TEMPLATE_WEB, "application.yml.ftl", mainResourcesFilePath, "bootstrap.yml");

        generateFile(params, Const.JPA_TEMPLATE_WEB, "ApplicationTests.java.ftl", testJavaFilePath, "ApplicationTests.java");

        generateFile(params, Const.JPA_TEMPLATE_WEB, "ServletInitializer.java.ftl", mainJavaFilePath, "ServletInitializer.java");

        String controllerName=new StringBuffer().append(moduleName.toUpperCase().substring(0, 1)).append(moduleName.substring(1)).toString()+"Controller";
        params.put("controllerName",controllerName);
        generateFile(params, Const.JPA_TEMPLATE_WEB, "ViewController.java.ftl", controllerFilePath,  controllerName+".java");

    }

    private void generateFile(Map<String, Object> params, String templatePath, String templateFile, String targetPath, String targetFile) {
        Configuration cfg = new Configuration();
        try {
            cfg.setDirectoryForTemplateLoading(new File(templatePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        File pd = new File(targetPath);
        if (!pd.exists()) {
            pd.mkdirs();
        }
        File f = new File(pd, targetFile);
        if (f.exists()) {
            log.warn("file {} exists skip!", f.getName());
            return;
        }
        Template temp = null;
        FileWriter fw = null;
        try {
            temp = cfg.getTemplate(templateFile);
            fw = new FileWriter(f);
            temp.process(params, fw);
            fw.flush();
            fw.close();
        } catch (IOException e) {
            log.error("generate {} failed!", f.getName());
        } catch (TemplateException e) {
            log.error("generate {} failed!", f.getName());
        } finally {
            temp = null;
            fw = null;
        }
    }

    private List<MySqlDbColumn> infoMySqlTable(String dbDriver, String dbUrl, String dbUserName, String dbPassword, String schema, String table) {
        log.info("do table [{}]", table);
        java.sql.Connection conn = null;
        PreparedStatement ps = null;
        java.sql.ResultSet rs = null;
        DatabaseMetaData dbmd = null;
        List<MySqlDbColumn> list = new LinkedList<MySqlDbColumn>();
        try {
            Class.forName(dbDriver);
            conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
            StringBuffer sqlColumn = new StringBuffer("");
            sqlColumn.append(" select * from information_schema.COLUMNS where 1=1 ");
            sqlColumn.append(" and table_schema='");
            sqlColumn.append(schema);
            sqlColumn.append("'");
            sqlColumn.append(" and table_name='");
            sqlColumn.append(table);
            sqlColumn.append("'");
            sqlColumn.append(" order by  ORDINAL_POSITION");

            PreparedStatement pstmt = conn.prepareStatement(sqlColumn.toString());
            rs = pstmt.executeQuery();
            while (rs.next()) {
                list.add(new MySqlDbColumn(rs));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(ps);
            JdbcUtils.closeConnection(conn);
        }

        StringBuffer sql = new StringBuffer("");
        sql.append(" select COLUMN_NAME from information_schema.COLUMNS where 1=1 ");
        sql.append(" and table_schema='");
        sql.append(schema);
        sql.append("'");
        sql.append(" and table_name='");
        sql.append(table);
        sql.append("'");
        sql.append(" and column_key='");
        sql.append("PRI");
        sql.append("'");
        sql.append(" order by  ORDINAL_POSITION");

        // JDBC模板依赖于连接池来获得数据的连接，所以必须先要构造连接池
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(dbDriver);
        dataSource.setUrl(dbUrl);
        dataSource.setUsername(dbUserName);
        dataSource.setPassword(dbPassword);

        // 创建JDBC模板
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 这里也可以使用构造方法
        jdbcTemplate.setDataSource(dataSource);
        List<String> pks = jdbcTemplate.queryForList(sql.toString(), String.class);
        if (pks != null && !pks.isEmpty()) {
            for (String k : pks) {
                for (MySqlDbColumn c : list) {
                    if (k.equalsIgnoreCase(c.getCode())) {
                        c.setPk(true);
                    }
                }
            }
        } else {
            log.warn("table {} 没有主键，生成后，请修改！！！");
        }
        return list;
    }
}
